// OpentTxl-C Version 11 error messages
// J.R. Cordy, Jan 2023

// Copyright 2023, James R. Cordy and others

// Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
// and associated documentation files (the “Software”), to deal in the Software without restriction, 
// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies 
// or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
// AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// TXL error message printing routines

// Modification Log

// v11.0 Initial revision, adapted from OpenTxl 11.0

// v11.3 Corrected spacing in pattern syntax error messages

#define COMPILER

// I/O, strings, memory allocation
#include "support.h"

// Global modules
#include "limits.h"
#include "options.h"
#include "tokens.h"
#include "trees.h"
#include "shared.h"
#include "idents.h"

// Check interface consistency
#include "errors.h"

// Source file names
// 1-origin [1 .. maxFiles]
string fileNames[maxFiles + 1];
int nFiles;                     // 0

// Stack exhaustion detection
#define FRAMESPACE 4096
addressint stackBase;

void setStackBase (void) {
    int dummy;
    stackBase = (addressint) ((&dummy) + FRAMESPACE);
}

void error (const string context, const string message, const int severity, const int code)
{
    string kind;
    stringcpy (kind, "E");
    if (severity == WARNING) {
        stringcpy (kind, "W");
    } else if (severity == INFORMATION) {
        stringcpy (kind, "I");
    }

    string qualifier;
    stringcpy (qualifier, "");
    if (severity == INFORMATION) {
        stringcpy (qualifier, "(Information) ");
    } else if (severity == WARNING) {
        stringcpy (qualifier, "(Warning) ");
    } else if ((severity == LIMIT_WARNING) || (severity == LIMIT_FATAL)) {
        stringcpy (qualifier, "(TXL implementation limit) ");
    } else if (severity == INTERNAL_FATAL) {
        stringcpy (qualifier, "(TXL internal error) ");
    }

    string TXL;
    stringcpy (TXL, "TXL");
    if (code < 1000) {
        stringcpy (TXL, "TXL0");
    }

    string comma;
    stringcpy (comma, "");
    string inputFileName;
    stringcpy (inputFileName, "");
    if ((phase == TRANSFORM) && (stringcmp (options_inputSourceFileName, "") != 0)) {
        stringcpy (comma, ", ");
        stringcpy (inputFileName, options_inputSourceFileName);
    }

    string space;
    stringcpy (space, "");
    if (stringcmp (context, "") != 0) {
        stringcpy (space, " ");
    };

    fprintf (stderr, "[%s%s%s] : %s%d%s%s%s - %s%s\n", inputFileName, comma, options_txlSourceFileName, 
        TXL, code, kind, space, context, qualifier, message);

    if (severity >= FATAL) {
        throw (QUIT);
    }
}

static void printInputTokens (const tokenIndexT errorTokenIndex)
{
    tokenIndexT startTokenIndex =  max (1, errorTokenIndex - 5);
    tokenIndexT endTokenIndex = min (errorTokenIndex + 5, lastTokenIndex - 1);

    fprintf (stderr, "\t");

    if (startTokenIndex > endTokenIndex) {
        fprintf (stderr, "EOF");

    } else {
        for (tokenIndexT tokenIndex = startTokenIndex; tokenIndex <= endTokenIndex; tokenIndex++) {
            if (tokenIndex == errorTokenIndex) {
                fprintf (stderr, ">>> %s <<< ", *ident_idents[inputTokens[tokenIndex].rawtoken]);
            } else {
                fprintf (stderr, "%s ", *ident_idents[inputTokens[tokenIndex].rawtoken]);
            }
        }
    }

    fprintf (stderr, "\n");
}

void syntaxError (const tokenIndexT errorTokenIndex)
{
    string context;

    if (errorTokenIndex >= lastTokenIndex) {
        stringprintf (context, "at end of %s", fileNames[inputTokens[lastTokenIndex].linenum / maxLines]);
        error (context, "Syntax error at end of:", DEFERRED, 191);
    } else {
        stringprintf (context, "line %d of %s", (inputTokens[errorTokenIndex].linenum % maxLines),  
            fileNames[inputTokens[errorTokenIndex].linenum / maxLines]);
        error (context, "Syntax error at or near:", DEFERRED, 192);
    }

    printInputTokens (errorTokenIndex);
    throw (QUIT);
}

void externalType (const string internalType, string resultType)
{
    string targetType;

    if (stringncmp (internalType, "list_1_", 7) == 0) {
        substring (targetType, internalType, 8, stringlen (internalType));
        stringprintf (resultType, "list %s+", targetType);

    } else if (stringncmp (internalType, "list_0_", 7) == 0) {
        substring (targetType, internalType, 8, stringlen (internalType));
        stringprintf (resultType, "list %s", targetType);

    } else if (stringncmp (internalType, "repeat_1_", 9) == 0) {
        substring (targetType, internalType, 10, stringlen (internalType));
        stringprintf (resultType, "repeat %s+", targetType);

    } else if (stringncmp (internalType, "repeat_0_", 9) == 0) {
        substring (targetType, internalType, 10, stringlen (internalType));
        stringprintf (resultType, "repeat %s", targetType);

    } else if (stringncmp (internalType, "opt__", 5) == 0) {
        substring (targetType, internalType, 6, stringlen (internalType));
        if (stringncmp (targetType, "lit__", 5) == 0) {
            substring (targetType, targetType, 6, stringlen (targetType));
            stringprintf (resultType, "opt '%s", targetType);
        } else {
            stringprintf (resultType, "opt %s", targetType);
        }

    } else if (stringncmp (internalType, "attr__", 6) == 0) {
        substring (targetType, internalType, 7, stringlen (internalType));
        if (stringncmp (targetType, "lit__", 5) == 0) {
            substring (targetType, targetType, 6, stringlen (targetType));
            stringprintf (resultType, "attr '%s", targetType);
        } else {
            stringprintf (resultType, "attr %s", targetType);
        }

    } else if (stringncmp (internalType, "lit__", 5) == 0) {
        substring (targetType, internalType, 6, stringlen (internalType));
        stringprintf (resultType, "lit '%s", targetType);

    } else if ((stringncmp (internalType, "__", 2) == 0) 
            && (stringlen (internalType) > 4)
            && (stringncmp (&(internalType[stringlen (internalType) - 2]), "__", 2) == 0)) {
        // e.g., __if_statement_2__
        substring (targetType, internalType, 3, stringlen (internalType) - 2);
        int i = stringlen (targetType);
        while (true) {
            if ((i == 0) || (targetType[i - 1] == '_')) break;
            i--;
        }
        if (i > 0) {
            substring (targetType, targetType, 1, i - 1);
        } 
        stringcpy (resultType, targetType);

    } else {
        stringcpy (resultType, internalType);
    }
}

void predefinedParseError (const tokenIndexT errorTokenIndex, const tokenT rulename, const tokenT callername, const tokenT typename_, const string source)
{
    string place;
    stringcpy (place, "at or near:");
    if (errorTokenIndex >= lastTokenIndex) {
        stringcpy (place, "at end of:");
    }

    string context;
    stringprintf (context, "predefined function [%s], called from [%s]", *ident_idents[rulename], *ident_idents[callername]); 

    string typestring, message;
    externalType (*ident_idents[typename_], typestring);
    stringprintf (message,  "Syntax error parsing %s as a [%s], %s", source, typestring, place);

    error (context, message, DEFERRED, 193);
    printInputTokens (errorTokenIndex);
    throw (QUIT);
}

#ifndef NOCOMPILE
#include "txltree.h"

static void printPatternToken (const treePT patternTP)
{
    if (patternTP == emptyTP) {
        // nothing to print

    } else if (stringcmp (*ident_idents[tree_trees[patternTP].name], "TXL_literal_") == 0) {
        fprintf (stderr, "%s", *ident_idents[txltree_literal_tokenT (patternTP)]);

    } else if (stringcmp (*ident_idents[tree_trees[patternTP].name], "TXL_firstTime_") == 0) {
        string typestring;
        externalType (*ident_idents[txltree_firstTime_typeT (patternTP)], typestring);
        fprintf (stderr, "%s [%s]", *ident_idents[txltree_firstTime_nameT (patternTP)], typestring);

    } else if (stringcmp (*ident_idents[tree_trees[patternTP].name], "TXL_expression_") == 0) {
        fprintf (stderr, "%s", *ident_idents[txltree_expression_baseT (patternTP)]);
        treePT ruleCallsTP = txltree_expression_ruleCallsTP (patternTP);
        while (true) {
            if (tree_plural_emptyP (ruleCallsTP)) break;
            treePT ruleCallTP = tree_plural_firstTP (ruleCallsTP);
            fprintf (stderr, "[%s", * ident_idents[txltree_ruleCall_nameT (ruleCallTP)]);
            treePT literalsTP = txltree_ruleCall_literalsTP (ruleCallTP);
            while (true) {
                if (tree_plural_emptyP (literalsTP)) break;
                fprintf (stderr, " %s", * ident_idents[txltree_literal_tokenT (tree_plural_firstTP (literalsTP))]);
                literalsTP = tree_plural_restTP (literalsTP);
            };
            fprintf (stderr, "]");
            ruleCallsTP = tree_plural_restTP (ruleCallsTP);
        }

    } else {
        error ("", "Fatal TXL error in printPatternToken", INTERNAL_FATAL, 194);
    }
}

void printPatternTokens (const tokenIndexT errorTokenIndex)
{
    tokenIndexT startTokenIndex =  max (1, (errorTokenIndex - 5));
    tokenIndexT endTokenIndex =  min ((errorTokenIndex + 5), (lastTokenIndex - 1));

    fprintf (stderr, "\t");
    if (startTokenIndex > endTokenIndex) {
        fprintf (stderr, "EOF");
    } else {
        for (tokenIndexT tokenIndex = startTokenIndex; tokenIndex <= endTokenIndex; tokenIndex++) { 
            if (tokenIndex == errorTokenIndex) {
                fprintf (stderr, ">>> ");
                printPatternToken ((inputTokens[tokenIndex].tree));
                fprintf (stderr, " <<<");
            } else {
                printPatternToken ((inputTokens[tokenIndex].tree));
                fprintf (stderr, " ");
            }
        }
    }
    fprintf (stderr, "\n");
}

void patternError (const tokenIndexT errorTokenIndex, const string context, const treePT productionTP)
{
    string place;
    stringcpy (place, "at or near:");
    if (errorTokenIndex >= lastTokenIndex) {
        stringcpy (place, "at end of:");
    }

    string typestring, message;
    externalType (*ident_idents[tree_trees[productionTP].name], typestring);
    stringprintf (message, "[%s] syntax error, %s", typestring, place);

    error (context, message, DEFERRED, 195);
    printPatternTokens (errorTokenIndex);
    throw (QUIT);
}
#endif

void parseInterruptError (const tokenIndexT errorTokenIndex, const bool patternParse, const string context)
{
    string place;
    stringcpy (place, "at or near:");
    if (errorTokenIndex >= lastTokenIndex) {
        stringcpy (place, "at end of:");
    }

    string message;
    #ifndef NOCOMPILE
    if (patternParse) {
        stringcpy (message, "Parse interrupted, "), stringcat (message, place);
        error (context, message, DEFERRED, 196);
        printPatternTokens (errorTokenIndex);
        throw (QUIT);
    }
    #endif

    stringprintf (message, "Parse interrupted, line %d of %s, %s", ((inputTokens[errorTokenIndex].linenum) % maxLines),  
        fileNames[inputTokens[errorTokenIndex].linenum / maxLines], place);
    error (context, message, DEFERRED, 197);
    printInputTokens (errorTokenIndex);
    throw (QUIT);
}

void parseStackError (const tokenIndexT errorTokenIndex, const bool patternParse, const string context)
{
    string place;
    stringcpy (place, "at or near:");
    if (errorTokenIndex >= lastTokenIndex) {
        stringcpy (place, "at end of:");
    }

    string message;
    #ifndef NOCOMPILE
    if (patternParse) {
        stringprintf (message, "Stack use limit (%lluk) reached, %s", (maxStackUse / 1024), place);
        error (context, message, DEFERRED, 198);
        printPatternTokens (errorTokenIndex);
        throw (QUIT);
    }
    #endif

    string lcontext;
    stringprintf (lcontext, "line %d of %s", ((inputTokens[errorTokenIndex].linenum) % maxLines),  
        fileNames[inputTokens[errorTokenIndex].linenum / maxLines]);
    stringprintf (message, "Stack use limit (%lluk) reached, %s", (maxStackUse / 1024), place);
    error (lcontext, message, DEFERRED, 199);
    printInputTokens (errorTokenIndex);
    throw (QUIT);
}

void errors (void)
{
    stringcpy (fileNames[0], "UNUSED");
    nFiles = 0;
    setStackBase ();
}

